package main

import (
	"context"
	"fmt"
	"sync"
	"time"
)

var wg sync.WaitGroup
var exit bool

func worker() {
	for true {
		fmt.Println("worker")
		time.Sleep(time.Second)
		if exit {
			break
		}
	}
	wg.Done()
}

func doWork() {
	wg.Add(1)
	go worker()
	time.Sleep(3 * time.Second)
	exit = true
	wg.Wait()
	fmt.Println("over")
}

func worker2(exitChan chan struct{}) {
LOOP:
	for true {
		fmt.Println("worker")
		time.Sleep(time.Second)
		select {
		case <-exitChan:
			break LOOP
		default:
		}
	}
	wg.Done()
}

func doWork2() {
	var exitChan = make(chan struct{})
	wg.Add(1)
	go worker2(exitChan)
	time.Sleep(3 * time.Second)
	exitChan <- struct{}{}
	close(exitChan)
	wg.Wait()
	fmt.Println("over")
}

func worker3(ctx context.Context) {
	go worker4(ctx)
LOOP:
	for true {
		fmt.Println("worker")
		time.Sleep(time.Second)
		select {
		case <-ctx.Done():
			break LOOP
		default:
		}
	}
	wg.Done()
}

func worker4(ctx context.Context) {
LOOP:
	for true {
		fmt.Println("worker2")
		time.Sleep(time.Second)
		select {
		case <-ctx.Done():
			break LOOP
		default:
		}
	}
}

func doWork3() {
	ctx, cancelFunc := context.WithCancel(context.Background())
	wg.Add(1)
	go worker3(ctx)
	time.Sleep(time.Second * 3)
	cancelFunc()
	wg.Wait()
	fmt.Println("over")

}

func gen(ctx context.Context) <-chan int {
	dst := make(chan int)
	n := 1
	go func() {
		for true {
			select {
			case <-ctx.Done():
				return
			case dst <- n:
				n++
			}
		}
	}()
	return dst
}
func doGen() {
	ctx, cancelFunc := context.WithCancel(context.Background())
	defer cancelFunc()
	for n := range gen(ctx) {
		fmt.Println(n)
		if n == 5 {
			break
		}
	}
}

func doDeadline() {
	d := time.Now().Add(50 * time.Microsecond)
	ctx, cancelFunc := context.WithDeadline(context.Background(), d)
	defer cancelFunc()
	select {
	case <-time.After(time.Microsecond * 10):
		fmt.Println("overslept")
	case <-ctx.Done():
		fmt.Println(ctx.Err())
	}
}

func worker5(ctx context.Context) {
LOOP:
	for true {
		fmt.Println("db connecting...")
		time.Sleep(time.Microsecond * 10)
		select {
		case <-ctx.Done():
			break LOOP
		default:
		}
	}
	fmt.Println("work5 done!")
	wg.Done()
}

func doWork5() {
	ctx, cancelFunc := context.WithTimeout(context.Background(), time.Millisecond*50)
	wg.Add(1)
	go worker5(ctx)
	time.Sleep(3 * time.Second)
	cancelFunc()
	wg.Wait()
	fmt.Println("over")
}

type TraceCode string

func worker6(ctx context.Context) {
	key := TraceCode("TRACE_CODE")
	traceCode, ok := ctx.Value(key).(string)
	if !ok {
		fmt.Println("invalid trace code")
	}
LOOP:
	for true {
		fmt.Printf("worker, trace code:%s\n", traceCode)
		time.Sleep(time.Millisecond * 10)
		select {
		case <-ctx.Done():
			break LOOP
		default:
		}
	}
	fmt.Println("worker done!")
	wg.Done()
}
func doWorker6() {
	ctx, cancelFunc := context.WithTimeout(context.Background(), time.Millisecond*50)
	ctx = context.WithValue(ctx, TraceCode("TRACE_CODE"), "12345")
	wg.Add(1)
	go worker6(ctx)
	time.Sleep(3 * time.Second)
	cancelFunc()
	wg.Wait()
	fmt.Println("over")
}

func main() {
	doWorker6()
}
